defmodule Pango.ImplWorker do
	@moduledoc false


	use GenServer
	require Logger
	@sync_time 60_000

	@spec start_link([atom]) :: {:ok, pid}
	def start_link(args) do
		GenServer.start_link(__MODULE__, args)
	end

	def init([server_name]) do
		#        :erlang.process_flag(:priority, :high)
		Process.send_after(self(), :push_statics, @sync_time)
		{:ok, %{:server => server_name}}
	end

	def handle_call(_, _, state) do
		{:reply, :ok, state}
	end


	def handle_cast({:sync, pool, worker, mod, func, args, from}, state) do
		t1 = :erlang.monotonic_time()
		reply = apply(__MODULE__, :do_mfa, [mod, func, args])
		t2 = :erlang.monotonic_time()
		:poolboy.checkin(pool, worker)
		GenServer.reply(from, reply)
		{:noreply, statics(state, mod, func, :erlang.convert_time_unit(t2 - t1, :native, :microsecond))}
	end
	# args 里有timeout 超时时间,可根据需要设置
	def handle_cast({:async, pool, worker, mod, func, args, from}, state) do
		Process.spawn(__MODULE__, :do_async, [mod, func, args, self(), from], [])
		:poolboy.checkin(pool, worker)
		{:noreply, state}
	end
	def handle_cast({:cast, pool, worker, mod, func, args, from}, state) do
		Process.spawn(__MODULE__, :do_async, [mod, func, args, self(), from], [])
		:poolboy.checkin(pool, worker)
		{:noreply, state}
	end

	def handle_info({:statics_async, mod, func, time}, state) do
		{:noreply, statics(state, mod, func, time)}
	end
	def handle_info(:push_statics, state) do
		state = sync(state)
		Process.send_after(self(), :push_statics, @sync_time)
		{:noreply, state}
	end

	def terminate(reason, state) do
		Logger.error(inspect({reason, state}))
		:ok
	end

	def do_async(mod, func, args, parent, from) do
		t1 = :erlang.monotonic_time()
		reply = apply(__MODULE__, :do_mfa, [mod, func, args])
		t2 = :erlang.monotonic_time()
		Process.send(parent, {:statics_async, mod, func, :erlang.convert_time_unit(t2 - t1, :native, :microsecond)}, [])
		from != nil && GenServer.reply(from, reply)
	end


	def do_mfa(mod, func, args) do
		try do
			case apply(mod, func, args) do
				{:ok, {:ok, resp}} -> # 事务
					{:ok, resp}
				{:ok, :ok} ->
					:ok
				{:ok, result} ->
					{:ok, result}
				:ok ->
					:ok
				{:error, remind, reason} ->
					Logger.error(inspect({mod, func, reason, args}))
					remind
				{:error, reason} ->
					reason
				other -> # 返回值
					{:ok, other}
			end
		rescue
			e in TipError ->
				case e do
					%{message: error_code, data: nil} ->
						error_code
					%{message: error_code, data: data} ->
						{:remind, error_code, data}
				end
			err ->
				str = "reason:#{inspect(err)},stacktrace:#{inspect(__STACKTRACE__)},"
				<> "mod:#{mod},func:#{func},args:#{inspect(args)},"
				Logger.error(str)
				"unexpected_error"
		catch
			{:error, reason, remind} ->
				Logger.error(inspect({mod, func, args, reason}))
				remind
			:ok ->
				:ok
			# 某些条件不满足, 返回提示信息给用户
			{:ok, _} = reply ->
				reply
			{:error, err} ->
				err
			{:remind, code, data} ->
				{:remind, code, data}
		end
	end

	def statics(state, mod, func, time) do
		mod_info = Map.get(state, mod, %{})
		value = Map.get(mod_info, func, %{"time" => 0, "count" => 0})
		        |> merge(%{"time" => round(div(time, 1000)), "count" => 1})
		Map.put(state, mod, Map.put(mod_info, func, value))
	end

	defp sync(%{:server => server_name} = state) do
		pid = Swarm.whereis_name(server_name)
		if is_pid(pid) && node(pid) == node() && Process.alive?(pid) do
			{_, rest} = Map.pop(state, :server, %{})
			Kernel.map_size(rest) > 0 && send(pid, {:push_statics, Map.delete(state, :server)})
			%{:server => server_name}
		else
			state
		end
	end

	def merge(map1, map2) do
		Map.merge(map1, map2, fn (_, v1, v2) -> v1 + v2 end)
	end

end